/**
 * 
 */
package com.web.sys.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.stereotype.Repository;

import com.web.sys.context.Pager;
import com.web.sys.context.SystemContext;
import com.web.sys.dao.IBaseDao;

/**

 * @author Administrator

 *

 */
@Repository("baseDao")
public class BaseDao<T> implements IBaseDao<T> {

	private SessionFactory sessionFactory;
	
	/**

	 * 创建一个class的对象来获取泛型的class

	 */
	private Class<T> clz;
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
    
	@Inject
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	@SuppressWarnings("unchecked")
	public Class<T> getClz(){
		if(clz==null){
			//获取泛型对象

			clz=(Class<T>) getSuperClassGenricType(this.getClass());	
		}
		return clz;
	}
	/** 

     * 通过反射,获得定义Class时声明的父类的泛型参数的类型. 如无法找到,返回Object.class 

     */  
    public static Class<Object> getSuperClassGenricType(final Class clz) {  
        Type type = clz.getGenericSuperclass();  
        if(!(type instanceof ParameterizedType)) {  
            return Object.class;  
        }  
        Type[] params = ((ParameterizedType)type).getActualTypeArguments();  
        if(!(params[0] instanceof Class)) {  
            return Object.class;  
        }  
        return (Class) params[0];  
    } 
	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}
	
	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#add(java.lang.Object)

	 */
	@Override
	public T add(T t) {
		getSession().save(t);
		return t;
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#update(java.lang.Object)

	 */
	@Override
	public void update(T t) {
		getSession().update(t);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#delete(java.lang.Object)

	 */
	@Override
	public void delete(String id) {
		getSession().delete(this.load(id));
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#load(java.lang.Object)

	 */
	@Override
	public T load(String id) {
		return (T)getSession().get(getClz(), id);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#list(java.lang.String, java.lang.Object[])

	 */
	@Override
	public List<T> list(String hql, Object[] args) {
		return this.list(hql, args, null);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#list(java.lang.String, java.lang.Object)

	 */
	@Override
	public List<T> list(String hql, Object arg) {
		return this.list(hql, new Object[] {arg});
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#list(java.lang.String)

	 */
	@Override
	public List<T> list(String hql) {
		return this.listByAlias(hql, null);
	}
	
	private String initSort(String hql){
		String order=SystemContext.getOrder();
		String sort=SystemContext.getSort();
		if(sort!=null && !"".equals(sort)){
			hql+=" order by "+sort;
			if(!"desc".equals(order)) hql+="asc";
			else hql+="desc";
		}
		return hql;
	}
	
	@SuppressWarnings("rawtypes")
	private void setAliasParameter(Query query ,Map<String ,Object> alias){
		if(alias!=null){
			Set<String> keys=alias.keySet();
			for(String key:keys){
				Object val=alias.get(key);
				if(val instanceof Collection){
					query.setParameterList(key, (Collection)val);
				} else {
					query.setParameter(key, val);
				}
			}
		}
	}

	private void setParameter(Query query,Object[] args){
		if(args!=null && args.length>0){
			int idx=0;
			for(Object arg:args){
				query.setParameter(idx++, arg);
			}
		}
	}
	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#list(java.lang.String, java.lang.Object[], java.util.Map)

	 */
	@Override
	public List<T> list(String hql, Object[] args, Map<String, Object> alias) {
		hql=initSort(hql);
		Query query=getSession().createQuery(hql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.list();
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#list(java.lang.String, java.util.Map)

	 */
	@Override
	public List<T> listByAlias(String hql, Map<String, Object> alias) {
		return this.list(hql, null, alias);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#find(java.lang.String, java.lang.Object[])

	 */
	@Override
	public Pager<T> find(String hql, Object[] args) {
		return this.find(hql, args, null);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#find(java.lang.String, java.lang.Object)

	 */
	@Override
	public Pager<T> find(String hql, Object arg) {
		return this.find(hql, new Object[] {arg});
		
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#find(java.lang.String)

	 */
	@Override
	public Pager<T> find(String hql) {
		return this.findByAlias(hql, null);
	}

	@SuppressWarnings("rawtypes")
	private void setPagers(Query query ,Pager pager){
		Integer pageSize= SystemContext.getPageSize();
		Integer pageOffset=SystemContext.getPageOffset();
		if(pageSize==null || pageSize<0) pageSize=20;
		if(pageOffset==null || pageOffset<0)pageOffset=0;
		pager.setOffset(pageOffset);
		pager.setSize(pageSize);
		query.setFirstResult(pageOffset).setMaxResults(pageSize);
	}
	
	private String getCountHql(String hql ,boolean isHql){
		String e=hql.substring(hql.indexOf("from"));
		String c="select count(*) "+e;
		if(isHql)
		  c.replaceAll("fetch", "");
		return c;
	}
	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#find(java.lang.String, java.lang.Object[], java.util.Map)

	 */
	@Override
	public Pager<T> find(String hql, Object[] args, Map<String, Object> alias) {
		String cq=getCountHql(hql,true);
		cq=initSort(cq);
		hql=initSort(hql);
		Query query=getSession().createQuery(hql);
		Query cquery=getSession().createQuery(cq);
		setAliasParameter(query, alias);
		setParameter(query, args);
		setAliasParameter(cquery, alias);
		setParameter(cquery, args);
		Pager<T> pager= new Pager<T>();
		setPagers(query,pager);
		List<T> datas=query.list();
		long total=(Long)cquery.uniqueResult();
		pager.setTotal(total);
		pager.setDatas(datas);
		return pager;
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#find(java.lang.String, java.util.Map)

	 */
	@Override
	public Pager<T> findByAlias(String hql, Map<String, Object> alias) {
		return this.find(hql, null, alias);
	}

	@Override
	public Object queryObject(String hql, Object[] args, Map<String, Object> alias) {
		Query query = getSession().createQuery(hql);
		setAliasParameter(query, alias);
		setParameter(query, args);
		return query.uniqueResult();
	}
	public Object[] query(String sql,Object...args){
		SQLQuery query = getSession().createSQLQuery(sql);
		setParameter(query, args);
		Object o=query.uniqueResult();
		if(o==null)return null;
		if(o instanceof Object[])return (Object[])o;
		return new Object[]{o};
	}
	 public  List<Object[]> queryList(String sql,Object...args){
		   SQLQuery query=getSession().createSQLQuery(sql);
		   setParameter(query, args);
	       List<Object[]> list=query.list();
	       return list;
	   }
	@Override
	public Object queryObjectByAlias(String hql,Map<String ,Object> alias){
		return this.queryObject(hql, null, alias);
	}
	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#queryObject(java.lang.String, java.lang.Object[])

	 */
	@Override
	public Object queryObject(String hql, Object[] args) {
		return this.queryObject(hql, args, null);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#queryObject(java.lang.String, java.lang.Object)

	 */
	@Override
	public Object queryObject(String hql, Object arg) {
		return this.queryObject(hql, new Object[] {arg});
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#queryObject(java.lang.String)

	 */
	@Override
	public Object queryObject(String hql) {
		return this.queryObject(hql, null);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#updateByHql(java.lang.String, java.lang.Object[])

	 */
	@Override
	public Object updateByHql(String hql, Object[] args) {
		Query query = getSession().createQuery(hql);
		setParameter(query, args);
		return query.uniqueResult();
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#updateByHql(java.lang.String, java.lang.Object)

	 */
	@Override
	public Object updateByHql(String hql, Object arg) {
		return this.queryObject(hql, new Object[] {arg});
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#updateByHql(java.lang.String)

	 */
	@Override
	public Object updateByHql(String hql) {
		return this.queryObject(hql, null);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#listBySql(java.lang.String, java.lang.Object[], java.lang.Class, boolean)

	 */
	@Override
	public List<Object> listBySql(String sql, Object[] args, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, args, null, clz, hasEntity);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#listBySql(java.lang.String, java.lang.Object, java.lang.Class, boolean)

	 */
	@Override
	public List<Object> listBySql(String sql, Object arg, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, new Object[] {arg}, clz, hasEntity);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#listBySql(java.lang.String, java.lang.Class, boolean)

	 */
	@Override
	public List<Object> listBySql(String sql, Class<Object> clz, boolean hasEntity) {
		return this.listBySql(sql, null, clz, hasEntity);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#listBySql(java.lang.String, java.lang.Object[], java.util.Map, java.lang.Class, boolean)

	 */
	@Override
	public List<Object> listBySql(String sql, Object[] args, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		sql=initSort(sql);
		SQLQuery sq= getSession().createSQLQuery(sql);
		setAliasParameter(sq, alias);
		setParameter(sq, args);
		if(hasEntity){
			sq.addEntity(clz);
		}else{
			sq.setResultTransformer(Transformers.aliasToBean(clz));
		}
		return sq.list();
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#listBySql(java.lang.String, java.util.Map, java.lang.Class, boolean)

	 */
	@Override
	public List<Object> listByAliasSql(String sql, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		return this.listBySql(sql, null, alias, clz, hasEntity);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#findBySql(java.lang.String, java.lang.Object[], java.lang.Class, boolean)

	 */
	@Override
	public Pager<Object> findBySql(String sql, Object[] args, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, args, null, clz, hasEntity);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#findBySql(java.lang.String, java.lang.Object, java.lang.Class, boolean)

	 */
	@Override
	public Pager<Object> findBySql(String sql, Object arg, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, new Object[] {arg}, clz, hasEntity);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#findBySql(java.lang.String, java.lang.Class, boolean)

	 */
	@Override
	public Pager<Object> findBySql(String sql, Class<?> clz, boolean hasEntity) {
		return this.findByAliasSql(sql, null, clz, hasEntity);
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#findBySql(java.lang.String, java.lang.Object[], java.util.Map, java.lang.Class, boolean)

	 */
	@Override
	public Pager<Object> findBySql(String sql, Object[] args, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		sql=initSort(sql);
		String cq=getCountHql(sql,false);
		SQLQuery query=getSession().createSQLQuery(sql);
		SQLQuery cquery=getSession().createSQLQuery(cq);
		setAliasParameter(query, alias);
		setParameter(query, args);
		setAliasParameter(cquery, alias);
		setParameter(cquery, args);
		Pager<Object> pager= new Pager<Object>();
		setPagers(query,pager);
		if(hasEntity){
			query.addEntity(clz);
		}else{
			query.setResultTransformer(Transformers.aliasToBean(clz));
		}
		List<Object> datas=query.list();
		Object total=(Object)cquery.uniqueResult();
		BigInteger bi=(BigInteger)total;
		
		//total.toString();

		pager.setTotal(bi.intValue());
		pager.setDatas(datas);
		pager.setOffset(3);
		pager.setSize(13);
		return pager;
	}

	/* (non-Javadoc)

	 * @see org.sun.hibernate.dao.IBaseDao#findBySql(java.lang.String, java.util.Map, java.lang.Class, boolean)

	 */
	@Override
	public Pager<Object> findByAliasSql(String sql, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		return this.findBySql(sql, null, alias, clz, hasEntity);
	}
	@Override
	public void execution(String sql,Object...args){
	    SQLQuery query=getSession().createSQLQuery(sql);
	    for(int i=0,size=args.length;i<size;i++){
	    	query.setParameter(i, args[i]);
	    }
	    query.executeUpdate();
	}

}